import core from '../index'

const symbol = {
    transformID: Symbol('transformID')
}

export default class Layer extends PIXI.DisplayObject {
    vertices = []
    winSize = [core.screen.width, core.screen.height]

    constructor(option) {
        super()
        this.raw = option.raw
        this.tileset = option.tileset
        this.minimap = this.generateMinimap()
        this.tilesheet = option.tilesheet
        this.tileSize = option.tileSize
        this.tilesheetSize = [option.tileset.imagewidth, option.tileset.imageheight]
        this.mapSize = [option.raw.width * this.tileSize[0], option.raw.height * this.tileSize[1]]

        // set vertices
        this.vertices[0] = 0
        this.vertices[1] = 0
        this.vertices[2] = this.mapSize[0]
        this.vertices[3] = 0
        this.vertices[4] = this.vertices[2]
        this.vertices[5] = this.mapSize[1]
        this.vertices[6] = 0
        this.vertices[7] = this.vertices[5]
    }

    calculateBounds() {
        this.calculateVertices()
        this._bounds.addQuad(this.vertices)
    }

    generateMinimap() {
        const
            minimap = new PIXI.Graphics(),
            {width, height, data} = this.raw,
            col = this.tileset.columns

        for (let i = 0; i < height; i++) {
            for (let j = 0; j < width; j++) {
                const
                    index = data[i * width + j] - 1,
                    x = index % col,
                    y = ~~(index / col)
                minimap.beginFill((x << 16) + (y << 8), index !== -1 ? 1 : 0)
                minimap.drawRect(j, i, 1, 1)
                minimap.endFill()
            }
        }

        return core.renderer.generateTexture(minimap, PIXI.SCALE_MODES.NEAREST)
    }

    destroy() {
        super.destroy()
        this.minimap.destroy()
    }

    removeTileAt(x, y) {
        this.raw.data[x + y * this.raw.width] = 0
        this.minimap.destroy()
        this.minimap = this.generateMinimap()
    }

    calculateVertices() {
        if (this[symbol.transformID] === this.transform._worldID) return
        this[symbol.transformID] = this.transform._worldID

        const
            {a, b, c, d, tx, ty} = this.transform.worldTransform,
            width = this.mapSize[0],
            height = this.mapSize[1],
            anchor = {x: this.pivot.x / width, y: this.pivot.y / height},
            w1 = -anchor.x * width,
            w0 = w1 + width,
            h1 = -anchor.y * height,
            h0 = h1 + height,
            vertices = this.vertices

        // xy
        vertices[0] = (a * w1) + (c * h1) + tx
        vertices[1] = (d * h1) + (b * w1) + ty

        // xy
        vertices[2] = (a * w0) + (c * h1) + tx
        vertices[3] = (d * h1) + (b * w0) + ty

         // xy
        vertices[4] = (a * w0) + (c * h0) + tx
        vertices[5] = (d * h0) + (b * w0) + ty

        // xy
        vertices[6] = (a * w1) + (c * h0) + tx
        vertices[7] = (d * h0) + (b * w1) + ty
    }

    renderWebGL(renderer) {
        super.renderWebGL(renderer)
        renderer.setObjectRenderer(renderer.plugins.tileLayer)
        renderer.plugins.tileLayer.render(this)
    }
}


class Renderer extends PIXI.ObjectRenderer {
    target = null

    onContextChange() {
        const gl = this.renderer.gl
        this.quad = new PIXI.Quad(gl)
        this.shader = new PIXI.Shader(gl, require('./layer.vert'), require('./layer.frag'))
    }

    upload() {
        this.target.calculateVertices()
        this.target.vertices.forEach((item, i) => {
            this.quad.vertices[i] = item
        })
        this.quad.upload()
    }

    render(target) {
        const {renderer, shader, quad} = this

        this.target !== target ? this.target = target : null
        this.upload()

        quad.initVao(shader)
        renderer.bindShader(shader)
        renderer.bindVao(quad.vao)

        shader.uniforms.uMinimap = renderer.bindTexture(target.minimap)
        shader.uniforms.uTilesheet = renderer.bindTexture(target.tilesheet)
        shader.uniforms.uTileSize = target.tileSize
        shader.uniforms.uTilesheetSize = target.tilesheetSize
        shader.uniforms.uTileSpace = target.tileset.spacing
        shader.uniforms.uTileMargin = target.tileset.margin
        shader.uniforms.uMapSize = target.mapSize
        shader.uniforms.uWinSize = target.winSize

        quad.vao.draw(renderer.gl.TRIANGLES, 6, 0)
    }

    destroy() {
        super.destroy()
        this.shader.destroy()
        this.quad.destroy()
    }
}

PIXI.WebGLRenderer.registerPlugin('tileLayer', Renderer)